package cn.clj1.builtInFunctions;

import cn.clj1.MyClojureInterp;
import cn.clj1.cell.CljCell;
import cn.clj1.cell.CljCellType;
import cn.clj1.recur.CljRecur;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Hashtable;
import java.util.List;

/**
 * 内置方法处理数学相关
 * */
public class CljMath {

    /**
     * 相加
     * */
    public static Number add(Number num1, Number num2) {
        if (num1 instanceof Integer || num1 instanceof Double || num1 instanceof Float || num1 instanceof Long || num1 instanceof Short || num1 instanceof Byte) {
            return num1.doubleValue() + num2.doubleValue();
        } else if (num1 instanceof BigInteger) {
            return ((BigInteger) num1).add(new BigInteger(num2.toString()));
        } else if (num1 instanceof BigDecimal) {
            return ((BigDecimal) num1).add(new BigDecimal(num2.toString()));
        } else {
            throw new IllegalArgumentException("Unsupported Number type");
        }
    }

    /**
     * 相减
     * */
    public static Number subtraction(Number num1, Number num2) {
        if (num1 instanceof Integer || num1 instanceof Double || num1 instanceof Float || num1 instanceof Long || num1 instanceof Short || num1 instanceof Byte) {
            return num1.doubleValue() - num2.doubleValue();
        } else if (num1 instanceof BigInteger) {
            return ((BigInteger) num1).subtract(new BigInteger(num2.toString()));
        } else if (num1 instanceof BigDecimal) {
            return ((BigDecimal) num1).subtract(new BigDecimal(num2.toString()));
        } else {
            throw new IllegalArgumentException("Unsupported Number type");
        }
    }

    /**
     * 相乘
     * */
    public static Number multiplication(Number num1, Number num2) {
        if (num1 instanceof Integer || num1 instanceof Double || num1 instanceof Float || num1 instanceof Long || num1 instanceof Short || num1 instanceof Byte) {
            return num1.doubleValue() * num2.doubleValue();
        } else if (num1 instanceof BigInteger) {
            return ((BigInteger) num1).multiply(new BigInteger(num2.toString()));
        } else if (num1 instanceof BigDecimal) {
            return ((BigDecimal) num1).multiply(new BigDecimal(num2.toString()));
        } else {
            throw new IllegalArgumentException("Unsupported Number type");
        }
    }

    /**
     * 除法
     * */
    public static Number division(Number num1, Number num2) {
        if (num1 instanceof Integer || num1 instanceof Double || num1 instanceof Float || num1 instanceof Long || num1 instanceof Short || num1 instanceof Byte) {
            return num1.doubleValue() / num2.doubleValue();
        } else if (num1 instanceof BigInteger) {
            return ((BigInteger) num1).divide(new BigInteger(num2.toString()));
        } else if (num1 instanceof BigDecimal) {
            return ((BigDecimal) num1).divide(new BigDecimal(num2.toString()));
        } else {
            throw new IllegalArgumentException("Unsupported Number type");
        }
    }

    /**
     * 加法
     * */
    public static CljCell Clj_list_addition(List<CljCell> lst, Hashtable<String, CljCell> funcHs, CljRecur cljRecur, CljCell rs) throws Exception {
        // + 加法
        Number number = 0;
        for (CljCell addCell: lst.subList(1, lst.size()))
        {
            if (addCell.getCljCellType() == CljCellType.number_clj) {
                number = add(number, (Number)addCell.getCell());
            } else if (addCell.getCljCellType() == CljCellType.symbol || addCell.getCljCellType() == CljCellType.percent) {
                CljCell addps = funcHs.get(addCell.getCell().toString());
                if (addps.getCljCellType() == CljCellType.number_clj) {
                    number = add(number, (Number)addps.getCell());
                } else if (addps.getCljCellType() == CljCellType.Clj_list) {
                    CljCell rs1 = MyClojureInterp.eval(addps, funcHs, cljRecur, rs);
                    if (rs1.getCljCellType() == CljCellType.number_clj) {
                        number = add(number, (Number)rs1.getCell());
                    } else {
                        throw new Exception("unexpected " + rs1.toString() + " 数据类型不能做加法运算！");
                    }
                }
                else {
                    throw new Exception("unexpected " + addCell.toString() + " 数据类型不能做加法运算！");
                }
            }
            else {
                CljCell rs1 = MyClojureInterp.eval(addCell, funcHs, cljRecur, rs);
                if (rs1.getCljCellType() == CljCellType.number_clj) {
                    number = add(number, (Number)rs1.getCell());
                } else {
                    throw new Exception("unexpected " + rs1.toString() + " 数据类型不能做加法运算！");
                }
            }
        }
        rs.setCljCellType(CljCellType.number_clj);
        rs.setCell(number);
        return rs;
    }

    /**
     * 减法
     * */
    public static CljCell Clj_list_subtraction(List<CljCell> lst, Hashtable<String, CljCell> funcHs, CljRecur cljRecur, CljCell rs) throws Exception {
        // - 减法
        //CljList sublst = toClj(lst.subList(1, lst.size()));
        List<CljCell> sublst = lst.subList(1, lst.size());
        CljCell subfirst = sublst.get(0);
        Number number3 = 0;
        if (subfirst.getCljCellType() == CljCellType.number_clj)
        {
            number3 = (Number)subfirst.getCell();
        }
        else if (subfirst.getCljCellType() == CljCellType.symbol || subfirst.getCljCellType() == CljCellType.percent)
        {
            CljCell subps = funcHs.get(subfirst.getCell().toString());
            if (subps.getCljCellType() == CljCellType.number_clj) {
                number3 = (Number)subps.getCell();
            } else if (subps.getCljCellType() == CljCellType.Clj_list) {
                CljCell rs1 = MyClojureInterp.eval((CljCell)subps.getCell(), funcHs, cljRecur, rs);
                if (rs1.getCljCellType() == CljCellType.number_clj) {
                    number3 = (Number)rs1.getCell();
                } else {
                    throw new Exception("unexpected " + rs1.toString() + " 数据类型不能做加法运算！");
                }
            }
            else {
                throw new Exception("unexpected " + subfirst.getCell().toString() + " 数据类型不能做加法运算！");
            }
        }
        else {
            throw new Exception("unexpected " + subfirst.getCell().toString() + " 数据类型不能做加法运算！");
        }

        for (CljCell subCell1: sublst.subList(1, sublst.size()))
        {
            if (subCell1.getCljCellType() == CljCellType.number_clj) {
                number3 = subtraction(number3, (Number)subCell1.getCell());
            } else if (subCell1.getCljCellType() == CljCellType.symbol || subCell1.getCljCellType() == CljCellType.percent) {
                CljCell subps1 = funcHs.get(subCell1.getCell().toString());
                if (subps1.getCljCellType() == CljCellType.number_clj) {
                    number3 = subtraction(number3, (Number)subps1.getCell());
                } else if (subps1.getCljCellType() == CljCellType.Clj_list) {
                    CljCell rs1 = MyClojureInterp.eval((CljCell)subps1.getCell(), funcHs, cljRecur, rs);
                    if (rs1.getCljCellType() == CljCellType.number_clj) {
                        number3 = subtraction(number3, (Number)rs1.getCell());
                    } else {
                        throw new Exception("unexpected " + subps1.getCell().toString() + " 数据类型不能做加法运算！");
                    }
                }
                else {
                    throw new Exception("unexpected " + subCell1.getCell().toString() + " 数据类型不能做加法运算！");
                }
            }
            else {
                CljCell rs1 = MyClojureInterp.eval(subCell1, funcHs, cljRecur, rs);
                if (rs1.getCljCellType() == CljCellType.number_clj) {
                    number3 = subtraction(number3, (Number)rs1.getCell());
                } else {
                    throw new Exception("unexpected " + rs1.getCell().toString() + " 数据类型不能做加法运算！");
                }
            }
        }
        rs.setCljCellType(CljCellType.number_clj);
        rs.setCell(number3);
        return rs;
    }

    /**
     * * 乘法
     * */
    public static CljCell Clj_list_multiplication(List<CljCell> lst, Hashtable<String, CljCell> funcHs, CljRecur cljRecur,CljCell rs) throws Exception {
        // * 乘法
        Number number2 = 1;
        for (CljCell multCell2: lst.subList(1, lst.size()))
        {
            if (multCell2.getCljCellType() == CljCellType.number_clj) {
                number2 = multiplication(number2, (Number)multCell2.getCell());
            } else if (multCell2.getCljCellType() == CljCellType.symbol || multCell2.getCljCellType() == CljCellType.percent) {
                CljCell multps2 = funcHs.get(multCell2.getCell().toString());
                if (multps2.getCljCellType() == CljCellType.number_clj) {
                    number2 = multiplication(number2, (Number)multps2.getCell());
                } else if (multps2.getCljCellType() == CljCellType.Clj_list) {
                    CljCell rs1 = MyClojureInterp.eval((CljCell)multps2.getCell(), funcHs, cljRecur, rs);
                    if (rs1.getCljCellType() == CljCellType.number_clj) {
                        number2 = multiplication(number2, (Number)rs1.getCell());
                    } else {
                        throw new Exception("unexpected " + multCell2.getCell().toString() + " 数据类型不能做加法运算！");
                    }
                }
                else {
                    throw new Exception("unexpected " + multCell2.getCell().toString() + " 数据类型不能做加法运算！");
                }
            }
            else {
                CljCell rs1 = MyClojureInterp.eval(multCell2, funcHs, cljRecur, rs);
                if (rs1.getCljCellType() == CljCellType.number_clj) {
                    number2 = multiplication(number2, (Number)rs1.getCell());
                } else {
                    throw new Exception("unexpected " + multCell2.getCell().toString() + " 数据类型不能做加法运算！");
                }
            }
        }
        rs.setCljCellType(CljCellType.number_clj);
        rs.setCell(number2);
        return rs;
    }

    /**
     * / 除法
     * */
    public static CljCell Clj_list_division(List<CljCell> lst, Hashtable<String, CljCell> funcHs, CljRecur cljRecur,CljCell rs) throws Exception {
        // / 除法
        Number number4 = 0;
        //CljList divlst = toClj(lst.subList(1, lst.size()));
        List<CljCell> divlst = lst.subList(1, lst.size());
        CljCell divfirst = divlst.get(0);
        if (divfirst.getCljCellType() == CljCellType.number_clj)
        {
            number4 = (Number)divfirst.getCell();
        }
        else if (divfirst.getCljCellType() == CljCellType.symbol || divfirst.getCljCellType() == CljCellType.percent)
        {
            CljCell divps = funcHs.get(divfirst.getCell().toString());
            if (divps.getCljCellType() == CljCellType.number_clj) {
                number4 = (Number)divps.getCell();
            } else if (divps.getCljCellType() == CljCellType.Clj_list) {
                CljCell rs1 = MyClojureInterp.eval((CljCell)divps.getCell(), funcHs, cljRecur, rs);
                if (rs1.getCljCellType() == CljCellType.number_clj) {
                    number4 = (Number)rs1.getCell();
                } else {
                    throw new Exception("unexpected " + divfirst.getCell().toString() + " 数据类型不能做加法运算！");
                }
            }
            else {
                throw new Exception("unexpected " + divfirst.getCell().toString() + " 数据类型不能做加法运算！");
            }
        }
        else {
            throw new Exception("unexpected " + divfirst.getCell().toString() + " 数据类型不能做加法运算！");
        }

        for (CljCell divCell1: divlst.subList(1, divlst.size()))
        {
            if (divCell1.getCljCellType() == CljCellType.number_clj) {
                number4 = division(number4, (Number)divCell1.getCell());
            } else if (divCell1.getCljCellType() == CljCellType.symbol || divCell1.getCljCellType() == CljCellType.percent) {
                CljCell divps1 = funcHs.get(divCell1.getCell().toString());
                if (divps1.getCljCellType() == CljCellType.number_clj) {
                    number4 = division(number4, (Number)divps1.getCell());
                } else if (divps1.getCljCellType() == CljCellType.Clj_list) {
                    CljCell rs1 = MyClojureInterp.eval((CljCell)divps1.getCell(), funcHs, cljRecur, rs);
                    if (rs1.getCljCellType() == CljCellType.number_clj) {
                        number4 = division(number4, (Number)rs1.getCell());
                    } else {
                        throw new Exception("unexpected " + divCell1.getCell().toString() + " 数据类型不能做加法运算！");
                    }
                }
                else {
                    throw new Exception("unexpected " + divCell1.getCell().toString() + " 数据类型不能做加法运算！");
                }
            }
            else {
                CljCell rs1 = MyClojureInterp.eval(divCell1, funcHs, cljRecur, rs);
                if (rs1.getCljCellType() == CljCellType.number_clj) {
                    number4 = division(number4, (Number)rs1.getCell());
                } else {
                    throw new Exception("unexpected " + divCell1.getCell().toString() + " 数据类型不能做加法运算！");
                }
            }
        }
        rs.setCljCellType(CljCellType.number_clj);
        rs.setCell(number4);
        return rs;
    }
}
